package com.lab.forthreadpool;


import com.lab.NoobChain;
import com.lab.util.StringUtil;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;

/**
 * 交易类
 *
 * @author weiwei
 */
public class Transaction {

    public String transactionId; //利用hash自己生成交易id
    public PublicKey sender; //发送方的公钥
    public PublicKey reciepient; //接收方的公钥
    public double value; //我们想要发给接收方的数量
    public byte[] signature; //signature签名

    public ArrayList<TransactionInput> inputs = null;
    public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();

    private static int sequence = 0; //交易次数

    // Constructor:
    public Transaction(PublicKey from, PublicKey to, double value, ArrayList<TransactionInput> inputs) {
        this.sender = from;
        this.reciepient = to;
        this.value = value;
        this.inputs = inputs;
    }

    public synchronized boolean processTransaction() {

        //验证signature签名
        if (verifySignature() == false) {
            System.out.println("#Transaction Signature failed to verify");
            return false;
        }

        //整合交易输入（确保他们未被使用）
        //Gathers transaction inputs (Making sure they are unspent):
        for (TransactionInput i : inputs) {
            i.UTXO = NoobChain.UTXOs.get(i.transactionOutputId);
        }

        //检查交易是不是有效
        //Checks if transaction is valid:
        if (getInputsValue() < NoobChain.minimumTransaction) {
            System.out.println("Transaction Inputs to small: " + getInputsValue());
            return false;
        }

        //Generate transaction outputs:
        double leftOver = getInputsValue() - value; //get value of inputs then the left over change:
        //交易的父类id
        transactionId = calulateHash();
        //给接收方发送value数量的货币
        outputs.add(new TransactionOutput(this.reciepient, value, transactionId));
        //给发送方"找零"
        outputs.add(new TransactionOutput(this.sender, leftOver, transactionId));

        //将生成的交易放到UTXOs中等待执行
        //Add outputs to Unspent list
        for (TransactionOutput o : outputs) {
            NoobChain.UTXOs.put(o.id, o);
        }

        //移除已经执行的交易
        for (TransactionInput i : inputs) {
            if (i.UTXO == null) continue; //if Transaction can't be found skip it
            NoobChain.UTXOs.remove(i.UTXO.id);
        }

        return true;
    }

    public double getInputsValue() {
        double total = 0;
        for (TransactionInput i : inputs) {
            if (i.UTXO == null) continue; //if Transaction can't be found skip it, This behavior may not be optimal.
            total += i.UTXO.value;
        }
        return total;
    }

    public void generateSignature(PrivateKey privateKey) {
        String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Double.toString(value);
        signature = StringUtil.applyECDSASig(privateKey, data);
    }


    public boolean verifySignature() {
        String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Double.toString(value);
        return StringUtil.verifyECDSASig(sender, data, signature);
    }

    public double getOutputsValue() {
        double total = 0;
        for (TransactionOutput o : outputs) {
            total += o.value;
        }
        return total;
    }

    private String calulateHash() {
        sequence++; //increase the sequence to avoid 2 identical transactions having the same hash
        return StringUtil.applySha256(
                StringUtil.getStringFromKey(sender) +
                        StringUtil.getStringFromKey(reciepient) +
                        Double.toString(value) + sequence
        );
    }
}
